package com.jinzhi.jzweb.utils;

import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.lionsoul.ip2region.xdb.Searcher;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * IP地址Util
 */
@Slf4j
public class IpAddressUtil {

    // ip2region.xdb 文件地址常量（本地xdb文件路径）
    public static String XDB_PATH = "ip2region.xdb";

    /**
     * 获取IP地址：
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("X-Forwarded-For");
            if (ipAddress != null && ipAddress.length() != 0 && !"unknown".equalsIgnoreCase(ipAddress)) {
                // 多次反向代理后会有多个ip值，第一个ip才是真实ip
                if (ipAddress.contains(",")) {
                    ipAddress = ipAddress.split(",")[0];
                }
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("HTTP_CLIENT_IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
            }
        } catch (Exception e) {
            log.error("获取IP地址异常,{}", e.getMessage());
        }
        return ipAddress;
    }

    /**
     * 获取mac地址
     */
    public static String getMacIpAddress() {
        try {
            InetAddress inetAddress = InetAddress.getLocalHost();
            byte[] macAddressBytes = NetworkInterface.getByInetAddress(inetAddress).getHardwareAddress();
            // 将mac地址拼装成String
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < macAddressBytes.length; i++) {
                if (i != 0) {
                    sb.append("-");
                }
                // mac[i] & 0xFF 是为了把byte转化为正整数
                String s = Integer.toHexString(macAddressBytes[i] & 0xFF);
                sb.append(s.length() == 1 ? 0 + s : s);
            }
            return sb.toString().trim().toUpperCase();
        } catch (Exception e) {
            log.error("Mac获取IP地址异常,{}", e.getMessage());
        }
        return "";
    }

    /**
     * 方法一：完全基于ip2region.xdb文件，对用户ip地址进行转换
     * 注：并发调用时，每个线程需创建一个独立的searcher对象 单独使用。
     */
    public static String getIpPossessionByFile(String ip) {
        if (StringUtils.isNotEmpty(ip)) {
            try {
                // 1、创建 searcher 对象
                Searcher searcher = Searcher.newWithFileOnly(XDB_PATH);
                // 2、查询
                long sTime = System.nanoTime();
                String region = searcher.search(ip);
                long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
                region = region.replace("|0", "");
                //log.info("{地区: {}, IO操作数: {}, 耗时: {} μs}", region, searcher.getIOCount(), cost);
                return region;
            } catch (Exception e) {
                log.error("获取IP地址异常：{} ", e.getMessage());
                throw new RuntimeException("获取IP地址异常");
            }
        }
        return "未知";
    }

    /**
     * 方法二：缓存 VectorIndex 索引，对用户ip地址进行转换
     * 注：每个线程需要单独创建一个独立的 Searcher 对象，但是都共享全局变量 vIndex 缓存。
     */
    public static String getCityInfoByVectorIndex(String ip) {
        if (StringUtils.isNotEmpty(ip)) {
            try {
                // 1、从 XDB_PATH 中预先加载 VectorIndex 缓存，并且作为全局变量，后续反复使用。
                byte[] vIndex = Searcher.loadVectorIndexFromFile(XDB_PATH);
                // 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。
                Searcher searcher = Searcher.newWithVectorIndex(XDB_PATH, vIndex);
                // 3、查询
                long sTime = System.nanoTime();
                String region = searcher.search(ip);
                long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
                region = region.replace("|0", "");
                //log.info("{地区: {}, IO操作数: {}, 耗时: {} μs}", region, searcher.getIOCount(), cost);
                return region;
            } catch (Exception e) {
                log.error("获取IP地址异常：{} ", e.getMessage());
                throw new RuntimeException("获取IP地址异常");
            }
        }
        return "未知";
    }

    /**
     * 方法三：缓存整个 xdb 数据，对用户ip地址进行转换
     * 注：并发使用时，用整个 xdb 数据缓存创建的查询对象可以安全的用于并发，也就是你可以把这个 searcher 对象做成全局对象去跨线程访问。
     */
    public static String getCityInfoByMemorySearch(String ip) {
        if (StringUtils.isNotEmpty(ip)) {
            try {
                // 1、从 XDB_PATH 加载整个 xdb 到内存。
                byte[] cBuff = Searcher.loadContentFromFile(XDB_PATH);
                // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
                Searcher searcher = Searcher.newWithBuffer(cBuff);
                // 3、查询
                long sTime = System.nanoTime();
                String region = searcher.search(ip);
                long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
                region = region.replace("|0", "");
                //log.info("{地区: {}, IO操作数: {}, 耗时: {} μs}", region, searcher.getIOCount(), cost);
                return region;
            } catch (Exception e) {
                log.error("获取IP地址异常：{} ", e.getMessage());
                throw new RuntimeException("获取IP地址异常");
            }
        }
        return "未知";
    }
    /**
     * new
     * @param request
     * @return
     */
    public static String getIpAddressNew(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            System.out.println("HTTP_X_FORWARDED_FOR:--------------"+ip);
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_CLUSTER_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_FORWARDED");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_VIA");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("REMOTE_ADDR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip.contains(",")) {
            return ip.split(",")[0];
        } else {
            return ip;
        }
    }

    /**
     * 方法四：在线获取IP地址
     * 注：通过别人或者官网提供的API接口去实现查询的功能，弊端就是特别依赖别人的服务器，一旦服务器宕机就无法访问了。
     */
    public static String getIpAddressByOnline(String ip) {

        try {
            //1、创建 URLConnction
            URL url = new URL("http://ip-api.com/json/" + ip + "?lang=zh-CN");

            //2、设置connection的属性
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(20000);
            connection.setReadTimeout(20000);
            connection.setRequestProperty("content-type", "application/json; charset=utf-8");

            //3.连接
            connection.connect();

            //4.获取内容
            InputStream inputStream = connection.getInputStream();

            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            String line;
            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            br.close();
            //System.out.println(sb);

            String str = sb.toString();
            if (StringUtils.isNotEmpty(str)) {
                // string转map
                Gson gson = new Gson();
                Map<String, Object> map = new HashMap<>();
                map = gson.fromJson(str, map.getClass());
                String country = (String) map.get("country");
                String city = (String) map.get("city");
                String regionName = (String) map.get("regionName");
                //log.info("【国家】{}，【城市】{}，【地区】{}", country, city, regionName);
                return country + "|" + city + "|" + regionName;
            }
        } catch (Exception e) {
            log.error("在线查询IP地址异常，{}", e.getMessage());
            throw new RuntimeException("在线查询IP地址异常");
        }
        return null;
    }

    /**
     * 根据IP地址 获取归属地
     */
    public static String getIpPossession(String ipAddress) {
        if (StringUtils.isNotEmpty(ipAddress)) {
            ipAddress = ipAddress.replace("|", " ");
            String[] cityList = ipAddress.split(" ");
            if (cityList.length > 0) {
                // 国内的显示到具体的省
                if ("中国".equals(cityList[0])) {
                    if (cityList.length > 1) {
                        return cityList[1];
                    }
                }
                // 国外显示到国家
                return cityList[0];
            }
        }
        return "未知";
    }

    //根据url获取内容
    public static String getStringWithUrl(String urlStr) throws IOException {

        String result = "";

        //建立连接
        URL url = new URL(urlStr);
        HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();
        httpUrlConn.setDoInput(true);
        httpUrlConn.setRequestMethod("GET");
        httpUrlConn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

        //获取输入流
        InputStream input = httpUrlConn.getInputStream();

        //将字节输入流转换为字符输入流
        InputStreamReader read = new InputStreamReader(input, "utf-8");

        //为字符输入流添加缓冲
        BufferedReader br = new BufferedReader(read);

        //读取返回结果
        String data = br.readLine();
        while (data != null) {
            result += data;
            data = br.readLine();
        }

        //释放资源
        br.close();
        read.close();
        input.close();
        httpUrlConn.disconnect();

        return result;

    }

    //获取ipAddress字符串
    public static String getIpAddressStr() throws IOException {
        return getStringWithUrl("http://pv.sohu.com/cityjson?ie=utf-8");
    }

    //获取公网ip
    public static String getIp() throws IOException {
        return getIpAddressStr().split("\"")[3];
    }

    //获取精确定位（根据公网ip）
    public static String getAddress() throws IOException {
        String str = getStringWithUrl("https://www.ipshudi.com/" + getIp() + ".htm").split("span")[3];
        return str.substring(1,str.length()-2);
    }

    //获取公网ip和精确定位地址（格式化）
    public static String getIpAndAddress() throws IOException {
        return "公网ip："+getIp()+"，当前地址定位："+getAddress();
    }

    public static String getOutIP() {
        try {
            URL whatismyip = new URL("http://checkip.amazonaws.com");
            BufferedReader in = new BufferedReader(new InputStreamReader(whatismyip.openStream()));

            String ip = in.readLine();
            return ip;

        } catch (Exception e) {}

        return "";
    }

    public static String getPublicIp() throws IOException {
        return getStringWithUrl("https://api.ipify.org/?format=json");
    }



    public static void main(String[] args) throws IOException {

        String ip = "118.248.216.8";// 国内IP
//        String abroadIp = "48.119.248.100"; // 国外IP

//        System.out.println("方法一（国内）：" + getIpPossessionByFile(ip));
//        System.out.println("方法二（国内）：" + getCityInfoByVectorIndex(ip));
//        System.out.println("方法三（国内）：" + getCityInfoByMemorySearch(ip));
//        System.out.println("方法四（国内）：" + getIpAddressByOnline(ip));
//        System.out.println("获取公网ip：" + getOutIP());
        System.out.println("获取公网ip1111：" + getIp());
        System.out.println("获取公网ip2222：" + getPublicIp());
        String outIP = getOutIP();
        System.out.println("方法（国内）：" + getIpAddressByOnline(outIP));


//        System.out.println("方法一（国外）：" + getIpPossessionByFile(abroadIp));
//        System.out.println("方法二（国外）：" + getCityInfoByVectorIndex(abroadIp));
//        System.out.println("方法三（国外）：" + getCityInfoByMemorySearch(abroadIp));
//        System.out.println("方法四（国外）：" + getIpAddressByOnline(abroadIp));

        //System.out.println("归属地（国内）：" + getIpPossession(getCityInfoByVectorIndex(ip)));
        //System.out.println("归属地（国外）：" + getIpPossession(getCityInfoByVectorIndex(abroadIp)));

    }


}